"""
类
"""

#类的关键字class
#类的名称首字母大写，多单词情况下每个单词首字母大写

class Person(object):
    name = 'winner'
    def dump(self):
        print(f'{self.name} is dumping')

new_person = Person() #类的实例化
print(new_person.name) #通过实例化进行属性调用
new_person.dump() #通过实例化进行函数调用

#类的参数self
#self是类函数中的必传参数，且必须放在第一个参数位置
#self是一个对象，它代表实例化的变量本身
#可以通过 self.name = 'winner' 来定义一个类变量
#self中的变量与含有self参数的函数可以在类中的任何一个函数内随意调用

class Person2(object):
    name = None
    def dump(self):
        print(f'{self.name} is dumping')
        def sleep():    #只可以在类函数内部定义局部函数时，可以不传self
            pass
        return sleep()
    def work(self):
        self.dump()

winner = Person2()
winner.name = 'winner' #更新对象属性
winner.dump()
winner.work()
winner.age = 18 #对象新定义的属性


#类的构造函数
class Test(object):
    def __init__(self,name,age=None):
        self.name = name
        self.age = age
    def run(self):
        print(f'my name is {self.name},my age is {self.age}')

test = Test(name='winner')
test.run()
test.name = 'winner2'
test.run()

#类中的私有函数与私有变量
#无法被实例化后的对象调用的类中的函数与变量
#类内部可以调用私有函数与变量
#只希望类内部调用，不希望被使用者调用
#定义方法：在变量或函数前加2个下横线，变量或函数名后边不能添加

class Person(object):
    def __init__(self,name):
        self.name = name
        self.__age = 18
    def dump(self):
        print(self.name,self.__age)
    def __cry(self):
        return 'I want cry'

print(dir(Person))
new_person = Person(name='winner')
print(dir(new_person))
print(new_person._Person__age)
print(new_person._Person__cry())

#python中封装的概念
#将不对外的私有属性或方法通过可以对外使用的函数而使用

class Parent(object):
    def __hello(self,data):
        print(f'hello,{data}')
    def helloworld(self):
        self.__hello('world')
p = Parent()
p.helloworld()

"""
装饰器
"""

#装饰器也是一种函数
#可以接受函数作为参数，内部对其处理，可以返回一个新函数

#装饰器的定义
def out(func_args):
    def inter(*args, **kwargs):
        print('目标函数执行之前=====')
        res = func_args(*args, **kwargs)
        print('目标函数执行之后=====')
    return inter

#使用方式1
def a(name):
    print(name)
out(a)('winner')

#使用方式2
@out
def b(name):
    print(name)
b('winner')


#类的常用装饰器
#classmethod 将类函数可以不经过实例化而直接被调用
class Test(object):
    @classmethod
    def add(cls,a,b):
        # classmethod装饰器函数中，cls中不能调用带self的函数，但拥有self函数的内部可以调用带classmethod装饰器函数
        return a+b
print(Test.add(1,2))

#staticmethod 将类函数可以不经过实例化而直接被调用，不传递self或cls参数，且该函数无法调用其他类函数或类变量
class Test(object):
    @staticmethod
    def add(a,b):
        return a+b
print(Test.add(1,2))

#property
class Test(object):
    def __init__(self,name):
        self.name = name
    @property
    def call_name(self):
        return f'hello,{self.name}'
    @call_name.setter
    def call_name(self,value):
        # self.name = value
        pass


test = Test('winner')
print(test.call_name)
test.name = 'winner3' #添加@call_name.setter装饰器定义同名call_name方法，该方法只有一个参数，之后就可以赋值
print(test.call_name)


"""
继承与多态
"""

#父类和子类：
#定义子类时，将父类传入到子类的参数内
#子类实例化可以调用自己与父类的函数与变量
#父类无法调用子类的函数与变量
class Parent(object):
    def __init__(self,name):
        self.name = name
    def talk(self):
        print(f'{self.name} talk')
    def think(self):
        print(f'{self.name} think')
class Child(Parent):
    def swimming(self):
        #如果父类属性是__name,则不可调用
        print(f'{self.name} == child can swimming')

c = Child('winner')
c.talk()
c.swimming()

# 多态：子类重写父类的方法，方法名称相同(为了保留子类该同名函数的功能)
# super:子类继承父类后，就可以使用的关键字
class MyParent(object):
    def talk(self,p):
        print(f'MyParent {p} talk')
class MyChild(MyParent):
    def talk(self,c,p):
        super(MyChild, self).talk(p) #调用父类方法
        #super().talk(p) super中的参数可以不传
        print(f'MyChild {c} talk, parent is {p}')
my_child = MyChild()
my_child.talk(c='winner', p='winnerFather')
#返回
#MyParent winnerFather talk
#MyChild winner talk, parent is winnerFather


#多重继承
class Car(object):
    def work(self):
        print('car work')
class Food(object):
    def work(self):
        print('food work')
class Cake(Car, Food): #如果有同名函数，最左边Car先发挥作用
    pass
cake = Cake()
cake.work() #car work
print(Cake.__mro__) #查看继承顺序

"""
类的高级函数
"""

#__str__一般返回对于该类的描述信息
class Test11(object):
    def __str__(self):
        return '这是关于这个类的描述'
test11 = Test11()
print(Test11) #<class '__main__.Test1'>
print(test11) #这是关于这个类的描述

#__getattr__当调用的属性或方法不存在时，会返回该方法定义的信息
class Test22(object):
    def __getattr__(self, key):
        print(f'这个key：{key}不存在')
        #return '111' 可以有返回值，也可以没有

test22 = Test22()
test22.a #这个key：a不存在
print(test22.a) #None

#__setattr__拦截当前类中不存在的属性与值
class Test33(object):
    def __setattr__(self, key, value): #key代表调用时的属性，value代表赋的值
        if key not in self.__dict__: #__dict__对象的内置字典
            self.__dict__[key] = value
test33 = Test33()
test33.a = 'winner'
print(test33.a)
print(test33.__dict__) #{'a': 'winner'}

#__call__将一个类变成一个函数
class Test44(object):
    def __call__(self, *args, **kwargs): #可传任意参数
        print(f'args is {kwargs}')
        return 'call返回值' #可以return也可以不return，与变成的函数情况相同
test44 = Test44()
test44(name='winner') #args is {'name': 'winner'}

#链式调用
class Test55(object):
    def __init__(self,attr=''):
        self.__attr = attr

    def __call__(self):
        print(f'key is {self.__attr}')

    def __getattr__(self, key):
        if self.__attr:
            key = f'{self.__attr}.{key}'
        else:
            key = key
        print(key)
        return Test55(key)
test55 = Test55()
test55.a.b.c
test55.a.d.e() #key is a.d.e
